package impl

import (
	"context"
	"errors"
	"fmt"
	"strings"

	"gitee.com/hexug/devcloud/cmdb/apps/resource"
	"gitee.com/hexug/devcloud/cmdb/common/logger"
	"gitee.com/hexug/devcloud/cmdb/common/validate"
	"gorm.io/gorm"
)

func (i *implServer) Put(ctx context.Context, in *resource.Resource) (out *resource.Resource, err error) {
	if err := validate.V().Struct(in); err != nil {
		return nil, err
	}
	tx := i.db.WithContext(ctx).Begin()
	defer func() {
		if err != nil {
			if rerr := tx.Rollback().Error; rerr != nil {
				logger.L().Errorf("数据库回滚失败：%s", rerr.Error())
			}
			logger.L().Errorf("数据库操作失败：%s", err.Error())
		} else {
			if cerr := tx.Commit().Error; cerr != nil {
				logger.L().Errorf("数据库事务提交失败：%s", cerr.Error())
			}
		}
	}()
	if len(in.Tags) > 0 {
		//先删掉第三方的标签
		err = tx.
			Model(&resource.Tag{}).
			Where(resource.Tag{
				Purpose: resource.TAG_PURPOSE_THIRDPARTY,
			}).
			Where("resource_id=?", in.Meta.Id).
			Delete(&resource.Tag{}).Error
		if err != nil {
			logger.L().Errorf("数据库删除第三方标签失败：%s", err.Error())
			return
		}
		//遍历tag，保存到数据库
		for _, tag := range in.Tags {
			d := struct {
				//一定要设置标签primaryKey，否则无法成功保存临时结构体
				ResourceId string `gorm:"column:resource_id;primaryKey"`
				*resource.Tag
			}{
				in.Meta.Id,
				tag,
			}
			err = tx.Save(d).Error
			if err != nil {
				logger.L().Errorf("标签保存失败：%s", err.Error())
				return
			}
		}

		//哈希 tag的部分
		var tags []*resource.Tag
		err = tx.Model(&resource.Tag{}).
			Where("resource_id=?", in.Meta.Id).
			Find(&tags).
			Error
		if err != nil {
			logger.L().Errorf("查询标签失败：%s", err.Error())
			return
		}
		var strTags []string
		for _, tag := range tags {
			strTags = append(strTags, tag.String())
		}
		res, serr := resource.MakeHash(strings.Join(strTags, ""))
		if serr != nil {
			err = serr
			logger.L().Errorf("标签hash失败：%s", serr.Error())
			return
		}
		in.ContentHash.TagHash = res
	}
	if err := in.MakeContentHash(); err != nil {
		return nil, err
	}
	//注意，这里需要注意err，一旦全局的err不为nil，就会回滚，所以在使用save的时候，无论中间出多少错，当最后的步骤成功了，一定要重置err值

	//先取出数据库中的数据作为参照

	var data resource.ContentHash
	err = tx.
		Model(&resource.Meta{}).
		Where("id=?", in.Meta.Id).
		Find(&data).Error
	if err != nil {
		logger.L().Errorf("查询ContentHash失败：%s", err.Error())
		return
	}
	//开始存储meta
	err = tx.
		// Model(&resource.Meta{}).
		Save(struct {
			*resource.Meta
			*resource.ContentHash
		}{
			in.Meta,
			in.ContentHash,
		}).Error
	if err != nil {
		logger.L().Errorf("meta保存失败：%s", err.Error())
		return
	}
	//开始存储spec
	if data.SpecHash != in.ContentHash.SpecHash {
		err = tx.
			Save(struct {
				ResourceId string `gorm:"column:id;primarykey"`
				*resource.Spec
			}{
				in.Meta.Id,
				in.Spec,
			}).Error
		if err != nil {
			logger.L().Errorf("Spec保存失败：%s", err.Error())
			return
		}
	}
	//开始存储cost
	if data.CostHash != in.ContentHash.CostHash {
		err = tx.
			Save(struct {
				ResourceId string `gorm:"column:id;primarykey"`
				*resource.Cost
			}{
				in.Meta.Id,
				in.Cost,
			}).Error
		if err != nil {
			logger.L().Errorf("cost保存失败：%s", err.Error())
			return
		}
	}
	//开始存储status
	if data.StatusHash != in.ContentHash.StatusHash {
		err = tx.
			Save(struct {
				ResourceId string `gorm:"column:id;primarykey"`
				*resource.Status
			}{
				in.Meta.Id,
				in.Status,
			}).Error
		if err != nil {
			logger.L().Errorf("status保存失败：%s", err.Error())
			return
		}
	}
	// fmt.Println(format.FormatToJson(in))
	return in, err
}

func (i *implServer) Delete(ctx context.Context, in *resource.DeleteRequest) (resou *resource.Resource, err error) {
	if err := validate.V().Struct(in); err != nil {
		return nil, err
	}
	tx := i.db.WithContext(ctx).Begin()
	defer func() {
		if err != nil {
			if rerr := tx.Rollback().Error; rerr != nil {
				logger.L().Errorf("数据库回滚失败：%s", rerr.Error())
			}
			logger.L().Errorf("数据库操作失败：%s", err.Error())
		} else {
			if cerr := tx.Commit().Error; cerr != nil {
				logger.L().Errorf("数据库事务提交失败：%s", cerr.Error())
			}
		}
	}()
	resou = resource.NewResource()
	//先查找对应的资源
	meta := resource.NewMeta()
	err = tx.Table("metas").
		Where("id=?", in.Id).
		First(meta).
		Error
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, errors.New("未找到对应的记录")
		}
		logger.L().Errorf("查找meta数据出错：%s", err.Error())
	} else {
		resou.Meta = meta
		ha := resource.NewContentHash()
		err = tx.Table("metas").
			Where("id=?", in.Id).
			First(&ha).
			Error
		if err == nil {
			resou.ContentHash = ha
		}
		//删除mata
		err = tx.Delete(&resource.Meta{Id: in.Id}).Error
		if err != nil {
			logger.L().Errorf("删除Meta失败：%s", err.Error())
			return
		}
	}
	spec := resource.NewSpec()
	err = tx.Table("specs").
		Where("id=?", in.Id).
		First(spec).
		Error
	if err != nil {
		// if err == gorm.ErrRecordNotFound {
		// 	return nil, errors.New("未找到对应的specs记录")
		// }
		logger.L().Errorf("查找specs数据出错：%s", err.Error())
	} else {
		resou.Spec = spec
		//删除spec
		err = tx.
			Where("id=?", in.Id).
			Delete(&resource.Spec{}).
			Error
		if err != nil {
			logger.L().Errorf("删除Spec失败：%s", err.Error())
			return
		}
	}
	cost := resource.NewCost()
	err = tx.Table("costs").
		Where("id=?", in.Id).
		First(cost).
		Error
	if err != nil {
		// if err == gorm.ErrRecordNotFound {
		// 	return nil, errors.New("未找到对应的costs记录")
		// }
		logger.L().Errorf("查找costs数据出错：%s", err.Error())
	} else {
		resou.Cost = cost
		// 删除cost
		err = tx.
			Where("id=?", in.Id).
			Delete(&resource.Cost{}).
			Error
		if err != nil {
			logger.L().Errorf("删除Cost失败：%s", err.Error())
			return
		}
	}
	status := resource.NewStatus()
	err = tx.Table("status").
		Where("id=?", in.Id).
		First(status).
		Error
	if err != nil {
		// if err == gorm.ErrRecordNotFound {
		// 	return nil, errors.New("未找到对应的status记录")
		// }
		logger.L().Errorf("查找status数据出错：%s", err.Error())
	} else {
		resou.Status = status
		// 删除status
		err = tx.
			Where("id=?", in.Id).
			Delete(&resource.Status{}).
			Error
		if err != nil {
			logger.L().Errorf("删除Status失败：%s", err.Error())
			return
		}
	}
	tags := []*resource.Tag{}
	err = tx.Table("tags").
		Where("resource_id=?", in.Id).
		Find(&tags).
		Error
	if err != nil {
		logger.L().Errorf("查找tag数据出错：%s", err.Error())
	} else {
		resou.Tags = tags
		//删除tag
		err = tx.
			Where("resource_id=?", in.Id).
			Delete(&resource.Tag{}).
			Error
		if err != nil {
			logger.L().Errorf("删除Tag失败：%s", err.Error())
			return
		}
	}
	return
}
func (i *implServer) Search(ctx context.Context, in *resource.SearchRequest) (*resource.ResourceSet, error) {
	if err := validate.V().Struct(in); err != nil {
		return nil, err
	}
	if in.PageNumber < 1 || in.PageNumber > 30 {
		in.PageNumber = 1
	}
	if in.PageSize < 1 || in.PageSize > 50 {
		in.PageSize = 5
	}
	query := i.db.
		WithContext(ctx).
		Select("*").
		Table("metas").
		Joins("left JOIN specs ON specs.id = metas.id").
		Joins("left JOIN costs ON costs.id = metas.id").
		Joins("left JOIN status ON status.id = metas.id").
		Offset(int((in.PageNumber - 1) * in.PageSize)).
		Limit(int(in.PageSize))
	if in.Domain != "" {
		query = query.Where("metas.domain=?", in.Domain)
	}
	if in.Namespace != "" {
		query = query.Where("metas.namespace=?", in.Namespace)
	}
	if in.Env != "" {
		query = query.Where("metas.env=?", in.Env)
	}
	if in.Vendor != nil {
		query = query.Where("specs.vendor=?", in.Vendor)
	}
	if in.Type != nil {
		query = query.Where("specs.type=?", in.Type)
	}
	if in.Owner != "" {
		query = query.Where("specs.owner=?", in.Owner)
	}
	if in.Keywords != "" {
		query = query.
			Where("specs.name like ? or  status.public_address like ? or status.private_address like ?",
				"%"+in.Keywords+"%",
				`%"`+in.Keywords+"%",
				`%"`+in.Keywords+"%",
			)
	}
	res := []*struct {
		*resource.Meta
		*resource.ContentHash
		*resource.Spec
		*resource.Status
		*resource.Cost
	}{}
	err := query.Find(&res).Error
	if err != nil {
		logger.L().Errorf("获取数据失败：%s", err.Error())
		return nil, err
	}
	rset := resource.NewResourceSet()
	for _, r := range res {
		//组装resour
		resour := resource.NewResource()
		resour.Meta.Id = r.Id
		resour.Meta.Domain = r.Domain
		resour.Meta.Namespace = r.Namespace
		resour.Meta.Env = r.Env
		resour.Meta.CreateAt = r.CreateAt
		resour.Meta.DeleteAt = r.DeleteAt
		resour.Meta.DeleteBy = r.DeleteBy
		resour.Meta.SyncAt = r.SyncAt
		resour.Meta.SyncBy = r.SyncBy
		resour.Meta.CredentialId = r.CredentialId
		resour.Meta.SerialNumber = r.SerialNumber

		resour.ContentHash.SpecHash = r.SpecHash
		resour.ContentHash.CostHash = r.CostHash
		resour.ContentHash.StatusHash = r.StatusHash
		resour.ContentHash.TagHash = r.TagHash
		resour.ContentHash.RelationHash = r.RelationHash
		resour.ContentHash.CustomHash = r.CustomHash

		resour.Spec.Vendor = r.Vendor
		resour.Spec.ResourceType = r.ResourceType
		resour.Spec.Region = r.Region
		resour.Spec.Zone = r.Zone
		resour.Spec.Owner = r.Owner
		resour.Spec.Name = r.Name
		resour.Spec.Category = r.Category
		resour.Spec.Type = r.Type
		resour.Spec.Description = r.Description
		resour.Spec.ExpireAt = r.ExpireAt
		resour.Spec.UpdateAt = r.UpdateAt
		resour.Spec.Cpu = r.Cpu
		resour.Spec.Gpu = r.Gpu
		resour.Spec.Memory = r.Memory
		resour.Spec.Storage = r.Storage
		resour.Spec.BandWidth = r.BandWidth
		resour.Spec.Extra = r.Extra

		resour.Cost.PayMode = r.PayMode
		resour.Cost.PayModeDetail = r.PayModeDetail
		resour.Cost.SalePrice = r.SalePrice
		resour.Cost.RealCost = r.RealCost
		resour.Cost.Policy = r.Policy
		resour.Cost.UnitPrice = r.UnitPrice

		resour.Status.Phase = r.Phase
		resour.Status.Describe = r.Describe
		resour.Status.LockMode = r.LockMode
		resour.Status.LockReason = r.LockReason
		resour.Status.PublicAddress = r.PublicAddress
		resour.Status.PrivateAddress = r.PrivateAddress

		//获取tag
		if in.WithTags {
			ts := []*resource.Tag{}
			fmt.Println("====")
			err = i.db.
				WithContext(ctx).
				Model(resource.Tag{}).
				Where("resource_id=?", r.Id).
				Find(&ts).Error
			if err != nil {
				logger.L().Errorf("获取Tag失败：%s", err.Error())
			} else {
				resour.Tags = ts
			}
		}
		rset.AddItem(resour)
		// fmt.Println(format.FormatToJson(r))
	}
	return rset, nil
}
